"""
Session utilities for managing user sessions with Redis
"""

from app import redis_client
from app.models.user import User
from app import db
import json
import time
from functools import wraps
from flask import request, g
from app.utils import ResponseUtil

class SessionManager:
    """Manager class for session operations"""
    
    SESSION_PREFIX = "user_session:"
    USER_SESSION_PREFIX = "user_sessions_map:"
    USER_INFO_PREFIX = "user_info:"
    SESSION_EXPIRY = 86400  # 24 hours
    
    @staticmethod
    def create_session(user_id, username):
        """
        Create a new session for a user
        
        Args:
            user_id (int): The user ID
            username (str): The username
            
        Returns:
            str: The session token
        """
        # Generate a session token
        import uuid
        session_token = str(uuid.uuid4())
        
        # Create session data
        session_data = {
            'user_id': user_id,
            'username': username,
            'login_time': int(time.time()),
            'last_accessed': int(time.time())
        }
        
        # Store session data in Redis
        redis_key = f"{SessionManager.SESSION_PREFIX}{session_token}"
        redis_client.setex(redis_key, SessionManager.SESSION_EXPIRY, json.dumps(session_data))
        
        # Track user sessions to ensure single session per user
        user_session_key = f"{SessionManager.USER_SESSION_PREFIX}{user_id}"
        old_session = redis_client.get(user_session_key)
        
        # If user had a previous session, delete it
        if old_session:
            old_session = old_session.decode('utf-8')
            old_session_key = f"{SessionManager.SESSION_PREFIX}{old_session}"
            redis_client.delete(old_session_key)
        
        # Store the new session token for this user
        redis_client.setex(user_session_key, SessionManager.SESSION_EXPIRY, session_token)
        
        # Cache user information in Redis
        SessionManager.cache_user_info(user_id)
        
        return session_token
    
    @staticmethod
    def cache_user_info(user_id):
        """
        Cache user information in Redis
        
        Args:
            user_id (int): The user ID
        """
        # Get user from database
        user = User.query.get(user_id)
        if user:
            # Create user data dictionary
            user_data = {
                'id': user.id,
                'username': user.username,
                'email': user.email,
                'status': user.status,
                'background_image': user.background_image,
                'avatar': user.avatar,
                'user_group': user.user_group,
                'created_at': user.created_at.isoformat() if user.created_at else None,
                'updated_at': user.updated_at.isoformat() if user.updated_at else None
            }
            
            # Store user data in Redis
            user_info_key = f"{SessionManager.USER_INFO_PREFIX}{user_id}"
            redis_client.setex(user_info_key, SessionManager.SESSION_EXPIRY, json.dumps(user_data))
    
    @staticmethod
    def get_cached_user_info(user_id):
        """
        Get cached user information from Redis
        
        Args:
            user_id (int): The user ID
            
        Returns:
            dict: The user data or None if not found
        """
        user_info_key = f"{SessionManager.USER_INFO_PREFIX}{user_id}"
        user_data = redis_client.get(user_info_key)
        
        if user_data:
            return json.loads(user_data)
        return None
    
    @staticmethod
    def update_cached_user_info(user_id):
        """
        Update cached user information in Redis
        
        Args:
            user_id (int): The user ID
        """
        SessionManager.cache_user_info(user_id)
    
    @staticmethod
    def get_session(token):
        """
        Retrieve session data by token
        
        Args:
            token (str): The session token
            
        Returns:
            dict: The session data or None if not found
        """
        redis_key = f"{SessionManager.SESSION_PREFIX}{token}"
        session_data = redis_client.get(redis_key)
        
        if session_data:
            return json.loads(session_data)
        return None
    
    @staticmethod
    def update_session(token):
        """
        Update the last accessed time of a session
        
        Args:
            token (str): The session token
            
        Returns:
            bool: True if session was updated, False if not found
        """
        redis_key = f"{SessionManager.SESSION_PREFIX}{token}"
        session_data = redis_client.get(redis_key)
        
        if session_data:
            session_data = json.loads(session_data)
            session_data['last_accessed'] = int(time.time())
            redis_client.setex(redis_key, SessionManager.SESSION_EXPIRY, json.dumps(session_data))
            return True
        return False
    
    @staticmethod
    def delete_session(token):
        """
        Delete a session
        
        Args:
            token (str): The session token
            
        Returns:
            bool: True if session was deleted, False if not found
        """
        # Get session data to find user_id
        redis_key = f"{SessionManager.SESSION_PREFIX}{token}"
        session_data = redis_client.get(redis_key)
        
        if session_data:
            session_data = json.loads(session_data)
            user_id = session_data.get('user_id')
            
            # Remove user session tracking
            if user_id:
                user_session_key = f"{SessionManager.USER_SESSION_PREFIX}{user_id}"
                redis_client.delete(user_session_key)
                
                # Also delete cached user info
                user_info_key = f"{SessionManager.USER_INFO_PREFIX}{user_id}"
                redis_client.delete(user_info_key)
            
            # Delete the session
            redis_client.delete(redis_key)
            return True
        return False
    
    @staticmethod
    def refresh_session(token):
        """
        Refresh a session's expiration time
        
        Args:
            token (str): The session token
            
        Returns:
            bool: True if session was refreshed, False if not found
        """
        redis_key = f"{SessionManager.SESSION_PREFIX}{token}"
        session_data = redis_client.get(redis_key)
        
        if session_data:
            # Refresh session
            redis_client.expire(redis_key, SessionManager.SESSION_EXPIRY)
            
            # Also refresh user session tracking
            session_data = json.loads(session_data)
            user_id = session_data.get('user_id')
            if user_id:
                user_session_key = f"{SessionManager.USER_SESSION_PREFIX}{user_id}"
                redis_client.expire(user_session_key, SessionManager.SESSION_EXPIRY)
                
                # Also refresh user info cache
                user_info_key = f"{SessionManager.USER_INFO_PREFIX}{user_id}"
                redis_client.expire(user_info_key, SessionManager.SESSION_EXPIRY)
            
            return True
        return False

def require_auth(f):
    """
    Decorator to require authentication for routes
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # Get token from Authorization header
        token = None
        if 'Authorization' in request.headers:
            auth_header = request.headers['Authorization']
            if auth_header.startswith('Bearer '):
                token = auth_header[7:]  # Remove 'Bearer ' prefix
        
        if not token:
            return ResponseUtil.error(message="Authorization token required", status_code=401)
        
        # Validate session
        session_data = SessionManager.get_session(token)
        if not session_data:
            return ResponseUtil.error(message="Invalid or expired session", status_code=401)
        
        # Update session last accessed time
        SessionManager.update_session(token)
        
        # Store session data in request context
        g.current_user_id = session_data['user_id']
        g.current_username = session_data['username']
        g.session_token = token
        
        return f(*args, **kwargs)
    
    return decorated_function